
package threads;

import componentes.Cliente;
import componentes.Configuracao;
import componentes.Servidor;
import excessoes.IpNaoEcontradoException;
import excessoes.ServicoDeBloqueioJaAtivo;
import excessoes.clienteException.ExecutarComandoLinuxException;
import excessoes.clienteException.ForaDoHorarioNavegacaoException;
import excessoes.clienteException.IpSemForwardException;
import excessoes.clienteException.MacSemForwardException;
import excessoes.clienteException.RecebeuLetrasException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import principal.Main;

/**
 *
 * @author Wagner Tavares
 */
public class Handler extends Thread{
    private Thread bloqueio;
    private Configuracao config;
    private Servidor servidor;
    private Hashtable<String,Cliente> clientes;
    //private Runnable consultaClientes;
    //private Runnable consultaServidor;
    
    private Thread liberacao;
    private Thread servicoDeBloqueio;
    private ServerSocket serverSocket;
    private Socket socket;
    
    public Handler(Configuracao config, Servidor servidor,Hashtable<String,Cliente> clientes,Thread servicoDeBloqueio){
        this.config = config;
        this.servidor = servidor;
        this.clientes = clientes;
        this.servicoDeBloqueio = servicoDeBloqueio;
    }
    @Override
    public void run(){
        try {            
            serverSocket = new ServerSocket(21345); 
            while(true){
                 socket = serverSocket.accept();
                 Thread subThread = new SubThread(socket);
                 subThread.start();
             }
        } catch (IOException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public class SubThread extends Thread{
        private Socket socket;
        public SubThread(Socket socket){
            this.socket = socket;
        }
        @Override
        public void run(){
            try {
                socket.setSoTimeout(15000);
                String[] args;
                ObjectInputStream ois;
                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                ois = new ObjectInputStream(socket.getInputStream());
                if (ois.readUTF().equals("Ola AutenticacaoServer")) {
                    oos.writeUTF("Ola AutenticacaoClient");
                    oos.flush();
                    args = (String[]) ois.readObject();
                    if (args != null && args.length > 0) {
                        char op = args[0].charAt(1);
                        switch (op) {
                            case '1': 
                                { 
                                    // reload clientes
                                    if (args.length > 1) {
                                        for (int i = 1; i < args.length; i++) {
                                            reload(args[i]);
                                        }
                                    } else {
                                        reload(null);
                                    }
                                    oos.writeUTF("Reload feito!");
                                    oos.flush();
                                    break;
                                }
                            case '2':
                                {
                                    // pára serviço de bloqueio
                                    Main.servicoDeBloqueioStop();
                                    oos.writeUTF("O Serviço de bloqueio foi parado.");
                                    oos.flush();
                                    break;
                                }
                            case '3':
                                {
                                    try {
                                        // iniciar serviço de bloqueio
                                        Main.servicoDeBloqueioStart();
                                        oos.writeUTF("O Serviço de bloqueio foi iniciado.");
                                        oos.flush();
                                    } catch (ServicoDeBloqueioJaAtivo ex) {
                                        Logger.getLogger(Main.class.getName()).logp(Level.WARNING, Main.class.getName(),"run()",ex.getMessage());
                                        oos.writeUTF(ex.getMessage());
                                        oos.flush();
                                    }
                                    break;
                                }
                            case '4':
                                {
                                    String retorno="";
                                    // libera grupo
                                    for (int i = 1; i < args.length; i++) {
                                        try {
                                            libera(args[i]);
                                            if(clientes.get(args[i]).getBloqueado()){
                                                retorno = retorno+"O ip "+args[i]+ " NÃO foi liberado, consulte os logs para maiores informações.";
                                            }
                                            else{
                                                retorno = retorno+"O ip "+args[i]+ " foi liberado.";
                                            }
                                        } catch (IpNaoEcontradoException ex) {
                                            Logger.getLogger(Main.class.getName()).logp(Level.WARNING, Handler.class.getName(), "run()", ex.getMessage()+" | "+ex.getIp());
                                            retorno = retorno+"O ip "+args[i]+ " não foi encontrado no Servidor de Autenticação.";
                                        }
                                    }
                                    if(retorno.equals(""))
                                        retorno = "Nada foi feito";
                                    oos.writeUTF(retorno);
                                    oos.flush();
                                    break;
                                }
                            case '5':
                                {
                                    // tenta liberar um
                                    try {
                                        tentaLiberar(args[1]);
                                        if(clientes.get(args[1]).getBloqueado()){
                                            oos.writeUTF("O ip "+args[1]+ " NÃO foi liberado, consulte os logs para maiores informações.");
                                            oos.flush();
                                        }
                                        else{
                                            oos.writeUTF("O ip "+args[1]+ " foi liberado");
                                            oos.flush();
                                        }
                                    } catch (IpNaoEcontradoException ex) {
                                        Logger.getLogger(Main.class.getName()).logp(Level.WARNING, Handler.class.getName(), "run()", ex.getMessage()+" | "+ex.getIp());
                                        oos.writeUTF("O ip "+args[1]+ " não foi encontrado no Servidor de Autenticação.");
                                        oos.flush();
                                    } catch (ForaDoHorarioNavegacaoException ex) {
                                        Logger.getLogger(Main.class.getName()).logp(Level.WARNING, Handler.class.getName(),"tentaLiberar()",ex.getMessage());
                                        oos.writeUTF("O ip "+args[1]+ " está fora do horário de navegação.");
                                        oos.flush();
                                    }
                                    break;
                                }
                            case '6':
                                {
                                    // liberar todos
                                    liberaTodos();
                                    oos.writeUTF("Todos os ips foram liberados, qualquer problema, favor olhar os logs.");
                                    oos.flush();
                                    break;
                                }
                            case '7':
                                {
                                    String retorno= "";
                                    // bloqueia grupo
                                    for (int i = 1; i < args.length; i++) {
                                        try {
                                            bloqueia(args[i]);
                                            if(clientes.get(args[i]).getBloqueado()){
                                                retorno = retorno+"O ip "+args[i]+ " foi bloqueado";
                                            }
                                            else{
                                                retorno = retorno+"O ip "+args[i]+ " NÃO foi bloqueado, consulte os logs para maiores informações.";
                                            }
                                        } catch (IpNaoEcontradoException ex) {
                                            Logger.getLogger(Main.class.getName()).logp(Level.WARNING, Handler.class.getName(), "run()", ex.getMessage()+" | "+ex.getIp());
                                            retorno = retorno+"O ip "+args[i]+ " não foi encontrado no Servidor de Autenticação.";
                                        }  
                                    }
                                    if(retorno.equals(""))
                                        retorno = "Nada foi feito";
                                    oos.writeUTF(retorno);
                                    oos.flush();
                                    break;
                                }
                            case '8':{
                                // bloqueia todos
                                bloqueiaTodos();
                                oos.writeUTF("Todos os ips foram bloqueados, qualquer problema, favor olhar os logs.");
                                oos.flush();
                            }
                        }
                    }
                    socket.close();
                    Main.serializar();
                }
            } catch (SocketException ex) {
                Logger.getLogger(Main.class.getName()).logp(Level.SEVERE, Handler.class.getName(), "run()", "Erro de conexão", ex);
            } catch (IOException ex) {
                Logger.getLogger(Main.class.getName()).logp(Level.SEVERE, Handler.class.getName(), "run()", "Erro de conexão", ex);
            } catch (ClassNotFoundException ex) {
                Logger.getLogger(Main.class.getName()).logp(Level.SEVERE, Handler.class.getName(), "run()", "Classe não encontrada! | "+ex.getCause(), ex);
            }
        }
    }
    private void bloqueia(String ip) throws IpNaoEcontradoException{
        if(clientes.containsKey(ip)){
            Cliente cliente = clientes.get(ip);
            bloqueio = new Bloqueio(cliente);
            bloqueio.run();
        }else
            throw new IpNaoEcontradoException(ip);
    }
    private void libera(String ip) throws IpNaoEcontradoException{
        if(clientes.containsKey(ip)){
            liberacao = new Liberacao(clientes.get(ip));
            liberacao.run();
        }else
            throw new IpNaoEcontradoException(ip);
    }
    private void tentaLiberar(String ip) throws IpNaoEcontradoException, ForaDoHorarioNavegacaoException{
        if(clientes.containsKey(ip)){
            try {
                clientes.get(ip).tentaLiberar();
            } catch (ExecutarComandoLinuxException ex) {
                Logger.getLogger(Main.class.getName()).logp(Level.SEVERE, Handler.class.getName(),"tentaLiberar()","Erro ao executar um comando linux: "+ex.getComando(), ex);
            } catch (IpSemForwardException ex) {
                Logger.getLogger(Main.class.getName()).logp(Level.WARNING, Handler.class.getName(),"tentaLiberar()",ex.getMessage()+": "+ex.getIp());
            } catch (MacSemForwardException ex) {
                Logger.getLogger(Main.class.getName()).logp(Level.WARNING, Handler.class.getName(),"tentaLiberar()",ex.getMessage()+": "+ex.getMac());
            } catch (RecebeuLetrasException ex) {
                Logger.getLogger(Main.class.getName()).logp(Level.SEVERE, Handler.class.getName(),"tentaLiberar()",ex.getMessage()+" | "+ex.getArrayProblema().toString(), ex);
            } 
        }else
            throw new IpNaoEcontradoException(ip);
    }
    private void reload(String ip){
        CarregaConfigs loaderConfs = new CarregaConfigs(config, servidor, clientes);
        loaderConfs.run();
        //Main.servicoDeBloqueioRestart();
    }
    private void liberaTodos(){
        Collection c = clientes.values();
        Iterator it = c.iterator();
        while(it.hasNext()){
            Cliente cliente = (Cliente) it.next();
            try {
                libera(cliente.getIp());   
            }catch (IpNaoEcontradoException ex) {
                Logger.getLogger(Main.class.getName()).logp(Level.WARNING, Handler.class.getName(), "liberaTodos()", ex.getMessage()+" | "+ex.getIp());
            } 
        }
    }
    private void bloqueiaTodos(){
        Collection c = clientes.values();
        Iterator it = c.iterator();
        while(it.hasNext()){
            Cliente cliente = (Cliente) it.next();
            try {
                bloqueia(cliente.getIp());   
            }catch (IpNaoEcontradoException ex) {
                Logger.getLogger(Main.class.getName()).logp(Level.WARNING, Handler.class.getName(), "bloqueiaTodos()", ex.getMessage()+" | "+ex.getIp());
            } 
        }
    }
}
